<!doctype html>
<meta charset="utf-8">
<body>
<script>
onload = function() {

log('Sanity tests:');
assertIterationTime('1s linear', [
  {is: null, at: -1},
  {is: 0, at: 0},
  {is: 0.5, at: 0.5},
  {is: 0.2, at: 0.2},
  {is: 0.8, at: 0.8},
  {is: null, at: 2},
]);


log('\nFill-mode:');
assertIterationTime('1s none', [
  {is: null, at: -1},
  {is: null, at: 2},
]);

assertIterationTime('1s both', [
  {is: 0, at: -1},
  {is: 1, at: 2},
]);

assertIterationTime('1s forwards', [
  {is: null, at: -1},
  {is: 1, at: 2},
]);

assertIterationTime('1s backwards', [
  {is: 0, at: -1},
  {is: null, at: 2},
]);


log('\nFill-mode + reverse direction:');
assertIterationTime('1s none reverse', [
  {is: null, at: -1},
  {is: null, at: 2},
]);

assertIterationTime('1s both reverse', [
  {is: 1, at: -1},
  {is: 0, at: 2},
]);

assertIterationTime('1s forwards reverse', [
  {is: null, at: -1},
  {is: 0, at: 2},
]);

assertIterationTime('1s backwards reverse', [
  {is: 1, at: -1},
  {is: null, at: 2},
]);


log('\nEnd exclusive timing:')
assertIterationTime('1s', [
  {is: null, at: 1},
]);


log('\nZero duration:')
assertIterationTime('none', [
  {is: null, at: -1},
  {is: null, at: 0},
  {is: null, at: 1},
]);

assertIterationTime('both', [
  {is: 0, at: -1},
  {is: 1, at: 0},
  {is: 1, at: 1},
]);

assertIterationTime('forwards', [
  {is: null, at: -1},
  {is: 1, at: 0},
  {is: 1, at: 1},
]);

assertIterationTime('backwards', [
  {is: 0, at: -1},
  {is: null, at: 0},
  {is: null, at: 1},
]);


log('\nZero duration + reverse direction:')
assertIterationTime('none reverse', [
  {is: null, at: -1},
  {is: null, at: 0},
  {is: null, at: 1},
]);

assertIterationTime('both reverse', [
  {is: 1, at: -1},
  {is: 0, at: 0},
  {is: 0, at: 1},
]);

assertIterationTime('forwards reverse', [
  {is: null, at: -1},
  {is: 0, at: 0},
  {is: 0, at: 1},
]);

assertIterationTime('backwards reverse', [
  {is: 1, at: -1},
  {is: null, at: 0},
  {is: null, at: 1},
]);


log('\nZero iterations:')
assertIterationTime('1s 0s 0 none', [
  {is: null, at: -1},
  {is: null, at: 0},
  {is: null, at: 0.5},
  {is: null, at: 2},
]);

assertIterationTime('1s 0s 0 both', [
  {is: 0, at: -1},
  {is: 0, at: 0},
  {is: 0, at: 2},
]);

assertIterationTime('1s 0s 0 forwards', [
  {is: null, at: -1},
  {is: 0, at: 0},
  {is: 0, at: 2},
]);

assertIterationTime('1s 0s 0 backwards', [
  {is: 0, at: -1},
  {is: null, at: 0},
  {is: null, at: 2},
]);


log('\nZero iterations, zero duration:')
assertIterationTime('0s 0s 0 none', [
  {is: null, at: -1},
  {is: null, at: 0},
  {is: null, at: 2},
]);

assertIterationTime('0s 0s 0 both', [
  {is: 0, at: -1},
  {is: 0, at: 0},
  {is: 0, at: 1},
]);

assertIterationTime('0s 0s 0 forwards', [
  {is: null, at: -1},
  {is: 0, at: 0},
  {is: 0, at: 1},
]);

assertIterationTime('0s 0s 0 backwards', [
  {is: 0, at: -1},
  {is: null, at: 0},
  {is: null, at: 1},
]);


log('\nMultiple iterations:')
assertIterationTime('1s 2 linear', [
  {is: null, at: -1},
  {is: 0, at: 0},
  {is: 0.4, at: 0.4},
  {is: 0, at: 1},
  {is: 0.6, at: 1.6},
  {is: null, at: 2},
]);

assertIterationTime('1s 2 linear reverse', [
  {is: 1, at: 0},
  {is: 0.6, at: 0.4},
  {is: 1, at: 1},
  {is: 0.4, at: 1.6},
]);

assertIterationTime('1s 2 linear alternate', [
  {is: 0, at: 0},
  {is: 0.4, at: 0.4},
  {is: 1, at: 1},
  {is: 0.4, at: 1.6},
]);

assertIterationTime('1s 2 linear alternate-reverse', [
  {is: 1, at: 0},
  {is: 0.6, at: 0.4},
  {is: 0, at: 1},
  {is: 0.6, at: 1.6},
]);


log('\nFractional iterations:')
assertIterationTime('1s 0.5 linear', [
  {is: null, at: -1},
  {is: 0, at: 0},
  {is: 0.2, at: 0.2},
  {is: null, at: 0.5},
]);

assertIterationTime('1s 1.5 linear', [
  {is: null, at: -1},
  {is: 0, at: 0},
  {is: 0.5, at: 0.5},
  {is: 0, at: 1},
  {is: 0.4, at: 1.4},
  {is: null, at: 1.5},
]);

assertIterationTime('1s 1.5 linear reverse', [
  {is: null, at: -1},
  {is: 1, at: 0},
  {is: 0.5, at: 0.5},
  {is: 1, at: 1},
  {is: 0.6, at: 1.4},
  {is: null, at: 1.5},
]);

assertIterationTime('1s 1.6 linear forwards', [
  {is: 0.6, at: 2},
]);

assertIterationTime('1s 1.6 linear forwards reverse ', [
  {is: 0.4, at: 2},
]);

assertIterationTime('1s 0.6 linear backwards reverse', [
  {is: 1, at: -1},
]);


log('\nInfinite iterations:')
assertIterationTime('1s infinite linear', [
  {is: 0, at: 1},
  {is: 0, at: 10},
  {is: 0, at: 1000},
  {is: 0.4, at: 1000.4},
]);


log('\nInfinite iterations, zero duration:')
assertIterationTime('0s infinite linear', [
  {is: null, at: -1},
  {is: null, at: 0},
  {is: null, at: 2},
]);

};

if (window.testRunner) {
  testRunner.dumpAsText();
}

var testElement = document.createElement('div');
document.body.appendChild(testElement);
testElement.style.left = 'auto';

function update() {
 document.body.offsetTop;
}
function log(message, klass) {
 message += '\n';
 var child;
 if (klass) {
   child = document.createElement('span');
   child.classList.add(klass);
   child.textContent = message;
 } else {
   child = document.createTextNode(message);
 }
 document.body.appendChild(child);
}

function assertIterationTime(animation, expectations) {
 testElement.style.setProperty('animation', 'invalid ' + animation);
 update();

 expectations.forEach(function(expectation) {
   if (expectation.is !== null && expectation.is != Math.round(expectation.is * 100) / 100) {
     console.log('ERROR: Test requires too much precision. ' + JSON.stringify(expectation));
     return;
   }
   testElement.style.setProperty('animation-name', 'invalid');
   update();

   var delay = expectation.at * -1;
   testElement.style.setProperty('animation-delay', delay + 's');
   testElement.style.setProperty('animation-name', 'test');
   update();

   var result = getComputedStyle(testElement).left;
   if (result === 'auto') {
     result = null;
   } else {
     result = Math.round(Number(result.replace(/px$/, '')) * 100) / 100;
   }
   if (result === expectation.is) {
     log('PASS: [' + animation + '] iteration time was [' + expectation.is + '] at ' + expectation.at + 's');
   } else {
     log('FAIL: [' + animation + '] iteration time was [' + result + '] at ' + expectation.at + 's' + ' expected [' + expectation.is + ']', 'fail');
   }
 });
}
</script>
<style>
body {
   white-space: pre;
   font-family: monospace;
}
.fail {
   font-weight: bold;
   color: red;
}
@keyframes test {
   0% {
       left: 0px;
   }
   100% {
       left: 1px;
   }
}
</style>
